package com.tt.job.api.log;

import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
import ch.qos.logback.classic.filter.LevelFilter;
import ch.qos.logback.core.rolling.RollingFileAppender;
import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP;
import ch.qos.logback.core.rolling.TimeBasedRollingPolicy;
import ch.qos.logback.core.spi.FilterReply;
import ch.qos.logback.core.util.FileSize;
import com.tt.job.api.Job;
import org.slf4j.LoggerFactory;

import java.util.Hashtable;
import java.util.Map;

public class JobLoggerFactory {

    public static ThreadLocal<Logger> LOGGER_FACTORY = new ThreadLocal<>();

    private static Hashtable<String, Logger> loggerTable = new Hashtable<>();

    public synchronized static Logger getLogger(String className) {
        if (loggerTable.containsKey(className)) {
            return loggerTable.get(className);
        }
        Logger logger = (Logger) LoggerFactory.getLogger(className);

        String jobName = className.substring(className.lastIndexOf(".") + 1);

        infoAppender(logger, jobName);
        errorAppender(logger, jobName);
        debugAppender(logger, jobName);

        logger.setLevel(Level.INFO);
        loggerTable.put(className, logger);

        return logger;
    }

    public static Logger getThreadLogger() {
        return LOGGER_FACTORY.get();
    }

    public static void setLoggers(Map<String, Job> jobMap, String level) {
        for (Map.Entry<String, Job> entry : jobMap.entrySet()) {
            Job job = entry.getValue();
            String className = job.getClass().getName();
            Logger logger = (Logger) LoggerFactory.getLogger(className);

            infoAppender(logger, entry.getKey());
            errorAppender(logger, entry.getKey());
            debugAppender(logger, entry.getKey());

            switch (level) {
                case "error":
                    logger.setLevel(Level.ERROR);
                    break;
                case "info":
                    logger.setLevel(Level.INFO);
                    break;
                case "debug":
                    logger.setLevel(Level.DEBUG);
                    break;
                default:
                    logger.setLevel(Level.INFO);
                    break;
            }

            loggerTable.put(className, logger);
        }
    }

    public static void infoAppender(Logger logger, String jobName) {
        LoggerContext loggerContext = logger.getLoggerContext();
        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setContext(loggerContext);
        encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level [%C] [%M] - %msg%n");
        encoder.start();

        RollingFileAppender appender = new RollingFileAppender();
        appender.setContext(loggerContext);
        TimeBasedRollingPolicy rollingPolicyBase = new TimeBasedRollingPolicy<>();
        rollingPolicyBase.setContext(loggerContext);
        rollingPolicyBase.setParent(appender);
        rollingPolicyBase.setFileNamePattern((String.format("logs/jobs/%s/%s", jobName, jobName) + "-info.%d{yyyy-MM-dd}.%i.log"));
        SizeAndTimeBasedFNATP sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP();
        sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(10 * FileSize.MB_COEFFICIENT));
        rollingPolicyBase.setTimeBasedFileNamingAndTriggeringPolicy(sizeAndTimeBasedFNATP);
        rollingPolicyBase.setMaxHistory(10);
        rollingPolicyBase.start();

        LevelFilter levelFilter = new LevelFilter();
        levelFilter.setLevel(Level.INFO);
        levelFilter.setOnMatch(FilterReply.ACCEPT);
        levelFilter.setOnMismatch(FilterReply.DENY);
        levelFilter.start();
        appender.addFilter(levelFilter);

        appender.setEncoder(encoder);
        appender.setRollingPolicy(rollingPolicyBase);
        appender.start();

        logger.setAdditive(false);
        logger.addAppender(appender);
    }

    public static void errorAppender(Logger logger, String jobName) {
        LoggerContext loggerContext = logger.getLoggerContext();
        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setContext(loggerContext);
        encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level [%C] [%M] - %msg%n");
        encoder.start();

        RollingFileAppender appender = new RollingFileAppender();
        appender.setContext(loggerContext);
        TimeBasedRollingPolicy rollingPolicyBase = new TimeBasedRollingPolicy<>();
        rollingPolicyBase.setContext(loggerContext);
        rollingPolicyBase.setParent(appender);
        rollingPolicyBase.setFileNamePattern((String.format("logs/jobs/%s/%s", jobName, jobName) + "-error.%d{yyyy-MM-dd}.%i.log"));
        SizeAndTimeBasedFNATP sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP();
        sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(10 * FileSize.MB_COEFFICIENT));
        rollingPolicyBase.setTimeBasedFileNamingAndTriggeringPolicy(sizeAndTimeBasedFNATP);
        rollingPolicyBase.setMaxHistory(10);
        rollingPolicyBase.start();

        LevelFilter levelFilter = new LevelFilter();
        levelFilter.setLevel(Level.ERROR);
        levelFilter.setOnMatch(FilterReply.ACCEPT);
        levelFilter.setOnMismatch(FilterReply.DENY);
        levelFilter.start();
        appender.addFilter(levelFilter);

        appender.setEncoder(encoder);
        appender.setRollingPolicy(rollingPolicyBase);
        appender.start();

        logger.setAdditive(false);
        logger.addAppender(appender);
    }

    public static void debugAppender(Logger logger, String jobName) {
        LoggerContext loggerContext = logger.getLoggerContext();
        PatternLayoutEncoder encoder = new PatternLayoutEncoder();
        encoder.setContext(loggerContext);
        encoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level [%C] [%M] - %msg%n");
        encoder.start();

        RollingFileAppender appender = new RollingFileAppender();
        appender.setContext(loggerContext);
        TimeBasedRollingPolicy rollingPolicyBase = new TimeBasedRollingPolicy<>();
        rollingPolicyBase.setContext(loggerContext);
        rollingPolicyBase.setParent(appender);
        rollingPolicyBase.setFileNamePattern((String.format("logs/jobs/%s/%s", jobName, jobName) + "-debug.%d{yyyy-MM-dd}.%i.log"));
        SizeAndTimeBasedFNATP sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP();
        sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(10 * FileSize.MB_COEFFICIENT));
        rollingPolicyBase.setTimeBasedFileNamingAndTriggeringPolicy(sizeAndTimeBasedFNATP);
        rollingPolicyBase.setMaxHistory(10);
        rollingPolicyBase.start();

        LevelFilter levelFilter = new LevelFilter();
        levelFilter.setLevel(Level.DEBUG);
        levelFilter.setOnMatch(FilterReply.ACCEPT);
        levelFilter.setOnMismatch(FilterReply.DENY);
        levelFilter.start();
        appender.addFilter(levelFilter);

        appender.setEncoder(encoder);
        appender.setRollingPolicy(rollingPolicyBase);
        appender.start();

        logger.setAdditive(false);
        logger.addAppender(appender);
    }
}
